import { App } from "@/models/app";
import { Component, ComponentTag } from "@/models/component";
import { Container } from "@/models/containers";
import { createUpdateId, EChart } from "@/models/reactiveComponent";
import { TCpId } from "@/models/types";


export type TComponentServices = ReturnType<typeof getServices>

export function getServices(app: App) {
    const mId2cpMap = new Map<string, Component>()
    const mUpdateId2cpMap = new Map<string, TCpId>()

    for (const cp of iterComponent(app)) {
        mId2cpMap.set(cp.id, cp)

        if (cp.tag === ComponentTag.EChart) {
            const chartCp = cp as EChart

            chartCp.chartInfos.forEach((info, idx) => {
                const updateId = createUpdateId(chartCp, idx)
                mUpdateId2cpMap.set(updateId, chartCp.id)
            })

        } else if (cp.tag === ComponentTag.Foreach) {
            mUpdateId2cpMap.set(cp.id, cp.id)

        } else if ('updateInfos' in cp) {
            mUpdateId2cpMap.set(cp.id, cp.id)
        }

        if ('visible' in cp && typeof cp['visible'] !== 'boolean') {
            mUpdateId2cpMap.set(cp.id, cp.id)
        }
    }


    function getComponent(id: string) {
        const cp = mId2cpMap.get(id)
        if (!cp) {
            throw new Error(`not found Component[id:${id}]`);
        }

        return cp
    }

    function getComponentByUpdateId(updateId: string) {
        const cpId = mUpdateId2cpMap.get(updateId)
        if (!cpId) {
            throw new Error(`not found updateId[${updateId}]`);
        }

        return getComponent(cpId)
    }

    function getApp() {
        return app
    }

    return {
        getComponent,
        getComponentByUpdateId,
        getApp,
    }
}





export function* iterComponent(app: App) {
    const stack = [app] as Container[]
    if (app.sizebar) {
        stack.push(app.sizebar)
    }

    while (stack.length > 0) {

        const target = stack.pop()!

        for (const c of target.children) {
            if ((c as Container).children) {
                stack.push(c as Container)
            }
            yield c
        }
    }

}